﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Service.Core.Identity.Model;
using Service.Core.Redis.Services;

namespace Service.Core.Identity.Handler
{
    /// <summary>
    /// 权限授权处理器 继承AuthorizationHandler ，并且需要一个权限必要参数
    /// </summary>
    public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
    {
        private readonly IHttpContextAccessor httpContextAccessor;
        private readonly IRedisService _redisService;

        /// <summary>
        /// 验证方案提供对象
        /// </summary>
        public IAuthenticationSchemeProvider Schemes { get; set; }


        /// <summary>
        /// 构造函数注入
        /// </summary>
        /// <param name="schemes"></param>
        /// <param name="httpContextAccessor"></param>
        public PermissionHandler(IAuthenticationSchemeProvider schemes,
            IHttpContextAccessor httpContextAccessor,
            IRedisService redisService)
        {
            Schemes = schemes;
            this.httpContextAccessor = httpContextAccessor;
            _redisService = redisService;
        }

        /// <summary>
        /// 重写异步处理程序
        /// </summary>
        /// <param name="context"></param>
        /// <param name="requirement"></param>
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
            PermissionRequirement requirement)
        {
            var ermissionsStr = await _redisService.Database.StringGetAsync(DefaultRedisKeys.PermissionKey);
            var permissions = JsonConvert.DeserializeObject<List<PermissionItem>>(ermissionsStr);
            requirement.Permissions = permissions;
            var httpContext = httpContextAccessor.HttpContext;

            if (httpContext != null)
            {
                //从AuthorizationHandlerContext转成HttpContext，以便取出表头信息
                //请求Url
                var questUrl = httpContext.Request.Path.Value?.ToLower();

                // 整体结构类似认证中间件UseAuthentication的逻辑，具体查看开源地址
                // https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authentication/Core/src/AuthenticationMiddleware.cs
                httpContext.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
                {
                    OriginalPath = httpContext.Request.Path,
                    OriginalPathBase = httpContext.Request.PathBase
                });

                //判断请求是否停止
                var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
                foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
                {
                    var handler =
                        await handlers.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler;
                    if (handler != null && await handler.HandleRequestAsync())
                    {
                        context.Fail();
                        return;
                    }
                }


                //判断请求是否拥有凭据，即有没有登录
                var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
                if (defaultAuthenticate != null)
                {
                    var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
                    //result?.Principal不为空即登录成功
                    if (result?.Principal != null)
                    {
                        httpContext.User = result.Principal;

                        // 获取当前用户的角色信息
                        var currentUserRoles = (from item in httpContext.User.Claims
                                                where item.Type == requirement.ClaimType
                                                select item.Value).ToList();

                        //如果是admin就不需要在验证一边权限了
                        if (!currentUserRoles.Contains(Permissions.SysAdmin))
                        {
                            //权限中是否存在请求的url
                            if (requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key?.ToLower() == questUrl)
                                    .Count() > 0)
                            {
                                //验证权限
                                if (currentUserRoles.Count <= 0 || requirement.Permissions.Where(w =>
                                        currentUserRoles.Contains(w.Role) && w.Url.ToLower() == questUrl).Count() <= 0)
                                {
                                    context.Fail();
                                    return;
                                }
                            }
                            else
                            {
                                context.Fail();
                                return;
                            }
                        }


                        //var exp = httpContext.User.Claims.SingleOrDefault(s => s.Type == JwtClaimTypes.Expiration)?.Value;

                        //long currentUnixTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();

                        ////判断过期时间
                        //if (long.Parse(exp) > currentUnixTimestamp)
                        //{
                        //    context.Succeed(requirement);
                        //}
                        //else
                        //{
                        //    context.Fail();
                        //    return;
                        //}
                        context.Succeed(requirement);
                        return;
                    }
                    else
                    {
                        context.Fail();
                        return;
                    }
                }
            }
        }
    }
}